#include <boost/unordered_map.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include <list>
#include <boost/any.hpp>
#include <exception>
#include <boost/format.hpp>

class StateException:public std::exception
{
private:
    std::wstring error_message;
    std::wstring source;
public:

    StateException(std::wstring error_message, std::wstring source)
    {
        this->error_message = error_message;
        this->source = source;
    }

    StateException(const StateException &src) throw ()
    {
        this->error_message = src.error_message;
        this->source = src.source;
    }

    virtual ~StateException() throw ()
    {

    }
};

class StateTransition;

class StateNode
{
private:
    std::wstring name;
    boost::unordered_map<std::wstring,boost::shared_ptr<StateNode> >linked_nodes;
    boost::unordered_map<std::wstring,boost::shared_ptr<StateTransition> >link_transitions;
public:
    std::wstring get_name()
    {
        return name;
    }

    bool has_link(std::wstring link)
    {
        return linked_nodes.find(link) != linked_nodes.end();
    }

    bool has_transition(std::wstring link)
    {
        return link_transitions.find(link) != link_transitions.end();
    }

    boost::shared_ptr<StateTransition> get_transition(std::wstring transition)
   {
        return link_transitions[transition];
   }

    boost::shared_ptr<StateNode> operator [] (std::wstring link)
    {
        if(linked_nodes.find(link) == linked_nodes.end())
        {
            throw StateException(L"No linked node",this->name);
        }

        return linked_nodes[link];
    }

    StateNode(std::wstring name)
    {
        this->name = name;
    }

    void AddLink(StateNode *link,StateTransition*transition)
    {
        AddLink(boost::shared_ptr<StateNode>(link),boost::shared_ptr<StateTransition>(transition));
    }

    void AddLink(boost::shared_ptr<StateNode> link,boost::shared_ptr<StateTransition> transition)
    {
        if( linked_nodes.find(link->name) == linked_nodes.end())
        {
            linked_nodes[link->name] = link;
            link_transitions[link->name] = transition;
        }
        else
        {
            throw StateException(L"Link already exists",this->name);
        }
    }
};

class StateTrigger
{
private:
    std::wstring state_from;
    std::wstring state_to;
public:
    virtual void on_start() {}
    virtual void on_transition() {}
    virtual void on_completion() {}
};


class StateTransitionController;

class StateValue
{
private:
    std::wstring name;
    StateTransitionController *controller;
    StateNode *current_node;
    boost::shared_ptr<StateTransition> current_transition;
    std::list<StateValue*> related_values;
public:

    std::wstring get_current_node_name()
    {
        return current_node->get_name();
    }
    StateValue(std::wstring name,StateNode *node,StateTransitionController *controller)
    {
        this->name = name;
        this->current_node = node;
        this->controller = controller;
    }

    virtual void update_node(boost::shared_ptr<StateNode> target)
    {
        current_node = target.get();
    }

    virtual void transition_finished()
    {
        current_transition.reset();
    }

    void start_transition(std::wstring name);
};


class StateTransition
{
private:
    StateValue *value;
public:

    boost::shared_ptr<StateNode> target_node;
    boost::shared_ptr<StateNode> fail_node;


    StateTransition(boost::shared_ptr<StateNode> target_node, boost::shared_ptr<StateNode> fail_node)
    {
        this->target_node = target_node;
        this->fail_node = fail_node;
    }

    StateTransition(boost::shared_ptr<StateNode> target_node)
    {
        this->target_node = target_node;
        this->fail_node.reset();
    }

    void set_value(StateValue *value)
    {
        this->value = value;
    }

    virtual void start()
    {
    }

    virtual bool is_complete()
    {
        return true;
    }

    virtual bool is_failed()
    {
        return false;
    }

    virtual void failed()
    {
        value->update_node(fail_node);
        value->transition_finished();
    }

    virtual void complete()
    {
        value->update_node(target_node);
        value->transition_finished();
    }

};


template <class T> boost::shared_ptr<StateTransition> copy_transition(boost::shared_ptr<T> transition)
{
    return boost::shared_ptr<StateTransition>( new T(*(transition.get())));
}

class StateTransitionController
{
private:
    std::list<StateTransition*>nodes;
public:

    void add_transition(StateTransition *transition)
    {
        nodes.push_back(transition);
    }

    void update()
    {
        std::list< std::list<StateTransition*>::iterator > for_removal;
        for( std::list<StateTransition*>::iterator it = nodes.begin(); it != nodes.end(); it++ )
        {

            if( (*it)->is_failed () )
            {
                (*it)->failed();
                for_removal.push_back(it);
            }
            else if((*it)->is_complete())
            {
                (*it)->complete();
                for_removal.push_back(it);
            }
        }

        for( std::list<std::list<StateTransition*>::iterator>::iterator it = for_removal.begin(); it != for_removal.end(); it++ )
        {
            nodes.erase(*it);
        }
    }

};
